package com.sky.filter;

import com.alibaba.fastjson.JSON;
import com.sky.constant.JwtClaimsConstant;
import com.sky.context.AlibabaTTL;
import com.sky.properties.JwtProperties;
import com.sky.result.Result;
import com.sky.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 *  全局过滤器  GlobalFilter                           过滤器 或 拦截器，抛出异常无法被全局异常捕获
 *  全局过滤器  "/*" 表示拦截所有请求
 *  需要在启动类上添加 @ServletComponentScan
 */
@Slf4j
@WebFilter(filterName="globalFilter", urlPatterns = {"/*"})
public class GlobalFilter extends OncePerRequestFilter {


    // mvc路径匹配器
    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    //需要放行的资源路径  Swagger的、登录接口、登出接口、WebSocket接口等
    String[] urls = {"/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/swagger-resources", "/v2/**", "/csrf",
                     "/admin/employee/login", "/admin/employee/logout",
                     "/ws/**", "/admin/websocket/paySuccessOrder", "/admin/websocket/reminder"};


    //注入token工具实体bean
    @Resource
    private JwtProperties jwtProperties;


    //过滤器拦截方法
    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        //System.out.println("GlobalFilter - 当前线程id：" + Thread.currentThread().getId());
        //System.out.println(jwtProperties);

        // 1.向下转型
        //HttpServletRequest request = (HttpServletRequest) servletRequest;
        //HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setContentType("application/json;charset=utf-8");


        // 2.获取请求路径
        log.info("GlobalFilter - 当前请求url: " + request.getRequestURI()); //swagger-resources/configuration/ui
        //System.out.println("url: " + request.getRequestURL()); //http://localhost:8888/swagger-resources/configuration/ui
        String requestURI = request.getRequestURI();


        // 3.校验当前路径是否需要放行
        Boolean flag = checkUrl(urls, requestURI);

        // 4.判断校验结果
        if(flag){ //true表示需要放行的路径
            log.info("过滤器放行前(需放行)：" + requestURI);
            filterChain.doFilter(request, response);
            log.info("过滤器放行后......");
            //结束方法，不执行下面的代码
            return;
        }

        // 不是放行路径，则校验
        // 5.获取token
        String jwtToken = request.getHeader(jwtProperties.getAdminTokenName());

        // 6.判断
        if( !StringUtils.hasLength(jwtToken) ){ //token为空
            log.info("过滤器-当前资源路径：{} 需要校验，但token为空，请先登录", requestURI);
            //封装token为空错误信息 并 写回
            Result<Object> error = Result.error("过滤器-token为空，请先登录");
            response.getWriter().println(JSON.toJSONString(error));
            //结束方法 下面代码不再执行
            return;
        }

        // 7.去除token前缀 "Bearer "
        String token = jwtToken.replace("Bearer ", "");

        // 8.解析token
        try {
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            Long eId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
            log.info("过滤器-token解析，员工id： {}, 存入threadLocal", eId);
            AlibabaTTL.threadLocalSet(eId);
        } catch (Exception e) {
            log.info(requestURI + " -- 过滤器-token令牌解析异常 ");
            //封装token解析错误信息 并 写回
            Result<Object> error = Result.error("过滤器-token解析异常，请重新登录");
            response.getWriter().println(JSON.toJSONString(error));
            //结束方法 下面代码不再执行
            return;
        }

        //已登录 放行
        log.info("GlobalFilter - " + requestURI + " - 令牌合法，放行");
        filterChain.doFilter(request, response);
        log.info("GlobalFilter - " + requestURI + " 过滤器放行后.... \n");
        //删除线程局部变量 防止内存泄露
        AlibabaTTL.threadLocalRemove();
        return;
    }


    //路径匹配方法
    private Boolean checkUrl(String[] urls, String requestURI) {
        for (String url : urls) {
            boolean flag = PATH_MATCHER.match(url, requestURI);
            if(flag){
                return true;
            }
        }
        return false;
    }


}
